/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;
import java.util.Stack;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.QName;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author yanzi
 *
 */
public class Uses extends DataDefElement {	
	private ArrayList<Augment> augments;
	private ArrayList<Refine> refines;
	
	
	
	

	/**
	 * 
	 */
	public Uses() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param env
	 * @param name
	 * @throws ModelException 
	 */
	public Uses(Env env, String name) throws ModelException {
		super(env, name);
		// TODO Auto-generated constructor stub
	}
	/**
	 * �ж���û��ѭ�����壬ѭ����������¼��������
	 * 1���ϲ�Ϊmodule����϶�û��ѭ������
	 * 2���ϲ�Ϊgrouping�����ж����е�����grouping�Ƿ��usesָ���
	 *    grouping��ͬ�������ͬ����Ϊѭ������
	 * 3��usesָ���ⲿ�����grouping,������λ��grouping�У����ⲿ
	 *    �����grouping����uses�˱�uses���ڵĶ���grouping������
	 *    ���ҲΪѭ������   
	 * @return
	 */
	private boolean checkLoopDefinition(){
		Grouping refGrouping = getRefGrouping();
		if (null == refGrouping){
			return false;
		}
		Grouping curGrouping = env.getCurGrouping();
		if (null == curGrouping){
			return false;
		}
		
		if (curGrouping.equals(this.getRefGrouping())){
			return true;
		}
		
		Stack<Grouping> groupings = curGrouping.getAncestorGroupings();
		if (null != groupings){
			if (!refGrouping.getEnv().getCurModule().equals(env.getCurModule())){
				if (null != refGrouping.searchUsesIncludeDescendant(groupings.peek())){
					return true;
				}
			}
			while(!groupings.isEmpty()){
				Grouping aGrouping = groupings.pop();
				if (aGrouping.equals(refGrouping)){
					return true;
				}
			}
		}
		
		
		return false;
	}
	public Grouping getRefGrouping(){
		if (null == name){
			return null;
		}
		QName qname = new QName(name);
		Module curModule = env.getCurModule();
		String localPrefix = null;
		if (curModule instanceof MainModule){
			localPrefix = ((MainModule)(curModule)).getPrefix();
		}
		else {
			BelongsTo b = (((SubModule)(curModule)).getBelongsto());
			MainModule mm = (MainModule) env.getModule(b.getName());
			if (null == mm){
				return null;
			}
			localPrefix = mm.getPrefix();
		}
		//�����ض����grouping�в���
		if (null == qname.getPrefix() 
				|| (localPrefix.equals(qname.getPrefix()))){
			return curModule.getGrouping(qname.getLocalName());
		}
		else {
			Module refModule = curModule.getModule(qname.getPrefix());
			return refModule.getGrouping(qname.getLocalName());
		}
	}
//	@Override
//	public boolean checkName(String name) {
//		if (null == name){
//			return false;
//		}
//		QName qname = new QName(name);
//		Module curModule = env.getCurModule();
//		String localPrefix = null;
//		if (curModule instanceof MainModule){
//			localPrefix = ((MainModule)(curModule)).getPrefix();
//		}
//		else {
//			BelongsTo b = (((SubModule)(curModule)).getBelongsto());
//			MainModule mm = (MainModule) env.getModule(b.getName());
//			if (null == mm){
//				return false;
//			}
//			localPrefix = mm.getPrefix();
//		}
//		//�����ض����grouping�в���
//		if (null == qname.getPrefix() 
//				|| (localPrefix.equals(qname.getPrefix()))){
//			if (null == curModule.getGrouping(qname.getLocalName())){
//				return false;
//			}
//		}
//		else {
//			Module refModule = curModule.getModule(qname.getPrefix());
//			if (null == refModule.getGrouping(qname.getLocalName())){
//				return false;
//			}
//		}
//		
//		return true;
//	}


	/**
	 * ��ȡaugment����Ŀ
	 * @return
	 * @see com.netscenery.model.Augment
	 */
	public int getAugmentsNumber(){
		if (null == this.augments){
			return 0;
		}
		return this.augments.size();
	}

	/**
	 * ���ָ������ƴ���augment
	 * @param name
	 * @return
	 * @throws ModelException 
	 * @see com.netscenery.model.Augment
	 */
	public Augment createAugment(String name) throws ModelException{
		if (null == name){
			return null;
		}
		
		return new Augment(newSubEnv(),name);
	}
	/**
	 * ���һ��Augment
	 * @param n
	 * @return
	 * @see com.netscenery.model.Augment
	 */
	public boolean addAugment(Augment n){
		if(null == n){
			return false;
		}
		
		if (null == this.augments){
			this.augments = new ArrayList<Augment>(1);
		}
		if (null != this.getAugment(n.getName())){
			return false;
		}
		return this.augments.add(n);
		

	}
	/**
	 * �����Ż�ȡAugment
	 * @param index
	 * @return
	 * @see com.netscenery.model.Augment
	 */
	public Augment getAugment(int index){
		if (null == this.augments){
			return null;
		}
		if (index >= this.augments.size()){
			return null;
		}
		return this.augments.get(index);
	}
	/**
	 * �����ƻ�ȡaugment
	 * @param name
	 * @return
	 * @see com.netscenery.model.Augment
	 */
	public Augment getAugment(String name){
		if (null == this.augments){
			return null;
		}
		int size = augments.size();
		if (size <= 0){
			return null;
		}
		
		for (int i = 0; i < size; i++){
			Augment n = this.augments.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return n;
				}
			}
		}
		return null;
	}
	/**
	 * ������ɾ��augment
	 * @param index
	 * @return Augment
	 * @see com.netscenery.model.Augment
	 */
	public Augment removeAugment(int index){
		if (null == this.augments){
			return null;
		}
		if (index >= this.augments.size()){
			return null;
		}
		return this.augments.remove(index);
	}
	/**
	 * ������ɾ��augment
	 * @param name
	 * @return Augment
	 * @see com.netscenery.model.Augment
	 */
	public Augment removeAugment(String name){
		if (null == this.augments){
			return null;
		}
		int size = augments.size();
		if (size <= 0){
			return null;
		}
		
		
		for (int i = 0; i < size; i++){
			Augment n = this.augments.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return augments.remove(i);
				}
			}
		}
		return null;
	}


	/**
	 * ��ȡrefine����Ŀ
	 * @return
	 * @see com.netscenery.model.Refine
	 */
	public int getRefinesNumber(){
		if (null == this.refines){
			return 0;
		}
		return this.refines.size();
	}
	
	/**
	 * ���һ��Refine
	 * @param n
	 * @return
	 * @see com.netscenery.model.Refine
	 */
	public boolean addRefine(Refine n){
		if(null == n){
			return false;
		}
		
		if (null == this.refines){
			this.refines = new ArrayList<Refine>(1);
		}
		if (null != this.getRefine(n.getName())){
			return false;
		}
		return this.refines.add(n);
		

	}
	/**
	 * �����Ż�ȡRefine
	 * @param index
	 * @return
	 * @see com.netscenery.model.Refine
	 */
	public Refine getRefine(int index){
		if (null == this.refines){
			return null;
		}
		if (index >= this.refines.size()){
			return null;
		}
		return this.refines.get(index);
	}
	/**
	 * �����ƻ�ȡRefine
	 * @param name
	 * @return
	 * @see com.netscenery.model.Refine
	 */
	public Refine getRefine(String name){
		if (null == this.refines){
			return null;
		}
		int size = refines.size();
		if (size <= 0){
			return null;
		}
		
		for (int i = 0; i < size; i++){
			Refine n = this.refines.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return n;
				}
			}
		}
		return null;
	}
	/**
	 * ������ɾ��Refine
	 * @param index
	 * @return Refine
	 * @see com.netscenery.model.Refine
	 */
	public Refine removeRefine(int index){
		if (null == this.refines){
			return null;
		}
		if (index >= this.refines.size()){
			return null;
		}
		return this.refines.remove(index);
	}
	/**
	 * ������ɾ��Refine
	 * @param name
	 * @return Refine
	 * @see com.netscenery.model.Refine
	 */
	public Refine removeRefine(String name){
		if (null == this.refines){
			return null;
		}
		int size = refines.size();
		if (size <= 0){
			return null;
		}
		
		
		for (int i = 0; i < size; i++){
			Refine n = this.refines.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return refines.remove(i);
				}
			}
		}
		return null;
	}
	
	public Node cloneNode(Env env,Node srcNode) throws ModelException{
		if (null == env){
			return null;
		}
		
		if (null == srcNode){
			return null;
		}
		
		YangStatement srcStatement = srcNode.writeYang();
		if (null == srcStatement){
			return null;
		}
		
		try {
			return (Node) Node.parseYANG(env, srcStatement);
		} catch (YangParserException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public Node getTargetNode(Augment aug){
		
		String[] nodes = parseDescendantPath(env,aug.getName());
		if (null == nodes){
			return null;
		}
		int size = nodes.length;
		Node node = null;
		for (int i = 0; i < size; i++){
			String nodeName = nodes[i];
			
			if (0 == i){

				QName qname = new QName(nodeName);
				node = this.getSchemaNodeFromGrouping(qname.getLocalName());			
			}
			else {
				
				QName qname = new QName(nodeName);
				
				node = node.getSchemaSon(qname.getLocalName());
			}
			if (null == node){
				return null;
			}
		}
		
		return  node;
	}
	/**
	 * ��ȡusesһ��grouping��ʵ����uses����
	 * ����������Ч�Ľڵ��б�
	 * @return
	 */
	public ArrayList<Node> getSchemaNodes(){
			
		ArrayList<Node> sons;
		try {
			sons = this.getSchemaNodesFromGrouping();
			if (null == sons){
				return null;
			}

			//augment
			if (0 != this.getAugmentsNumber()){
				int augSize = this.getAugmentsNumber();
				
				for (int i = 0; i < augSize; i++){
					Augment aug = this.getAugment(i);
					if (null == aug){
						continue;
					}
					
					//�ҵ�Ŀ��ڵ�
					Node targetNode = getTargetNode(aug);
					ArrayList<Node> augSchemaNodes = aug.getDataDefs().getSchemaNodes();
					for (int j = 0; j < augSchemaNodes.size();j++){
						Node augNode = augSchemaNodes.get(j);
						if (null == augNode){
							continue;
						}
						
						targetNode.addSon(augNode);
					}
				}
				
			}
			//refine
			
			return sons;
		} catch (ModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
		//TODO:��ʱ��ʵ�֣���ģ�ͽ�����û��̫������
		
		
	}
	
	public ArrayList<Node> getSchemaNodesFromGrouping() throws ModelException{
		
		Grouping grouping = this.getRefGrouping();		
		if (null == grouping ){
			return null;
		}
		
		ArrayList<Node> nodes = grouping.getSchemaNodes();
		if (null ==nodes){
			return null;
		}
		ArrayList<Node> sons= null;
		Env subEnv = newSubEnv();
		subEnv.setParent(env.getParent());
		subEnv.setRefGrouping(grouping);
	
		
		int size = nodes.size();
		
		for (int i = 0; i < size; i++){
			Node node = nodes.get(i);
			if (null == node){
				continue;
			}
			
			Node sonNode= cloneNode(subEnv, node);
			if (null != sonNode){
				if (null == sons){
					sons = new ArrayList<Node>(1);
				}
				
				sons.add(sonNode);
			}
		}	
		return sons;
		
		
	}
	
	public Node getSchemaNode(String name){
		ArrayList<Node> nodes = getSchemaNodes();
		if (null == nodes){
			return null;
		}
		
		int size = nodes.size();
		for (int i = 0; i < size;i++){
			Node node= nodes.get(i);
			if (node.getName().equals(name)){
				return node;
			}
		}
		
		return null;
	}
	public Node getSchemaNodeFromGrouping(String name){
		ArrayList<Node> nodes;
		try {
			nodes = getSchemaNodesFromGrouping();
			if (null == nodes){
				return null;
			}
			
			int size = nodes.size();
			for (int i = 0; i < size;i++){
				Node node= nodes.get(i);
				if (node.getName().equals(name)){
					return node;
				}
			}
			
			return null;
		} catch (ModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
		
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		if (checkLoopDefinition()){
			validator.setIsValid(false);
			validator.addMsg(new Message(this, ErrTag.ERROR, "loop reference is detected!"));
		}
		if (0 != this.getAugmentsNumber()){
			for (int i = 0; i< this.getAugmentsNumber();i++){
				Augment augment = getAugment(i);
				if (null != augment){
					Validator aValidator = augment.validate();
					validator.copy(aValidator);
				}
			}
		}
		Validator sv = super.validate();
		validator.copy(sv);
		return validator;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {
		YangStatement uses= new YangStatement(YangKeyword.USES.getKeyword(),this.name);
		if (!isNullString(getDescription())){
			YangStatement descr = new YangStatement(YangKeyword.DESCRIPTION.getKeyword(),getDescription());
			uses.addChild(descr);
		}
		if (!isNullString(getReference())){
			YangStatement ref = new YangStatement(YangStatement.YANG_STATEMENT_REFERENCE,this.getReference());
			uses.addChild(ref);
		}
		
		if (Status.CURRENT != getStatus()){
			YangStatement status = new YangStatement(YangStatement.YANG_STATEMENT_STATUS,this.getStatus().getStatus());
			uses.addChild(status);
		}
		if (!isNullString(this.when)){
			YangStatement when = new YangStatement(YangKeyword.WHEN.getKeyword(),this.when);
			uses.addChild(when);
		}
		
		if (null != if_features){
			int size = this.getIf_featuresNumber();
			
			for (int i = 0; i < size; i++){
				String feature = this.getIf_feature(i);
				if (null != feature){
					YangStatement  featureState = 
							new YangStatement(
									YangStatement.YANG_STATEMENT_IFFEATURE
							        ,feature);
					uses.addChild(featureState);
				}
			}
		
		}
		if (null != this.augments){
			int size = this.getAugmentsNumber();
			
			for (int i = 0; i < size; i++){
				Augment aug = this.getAugment(i);
				if (null != aug){
					YangStatement augment = aug.writeYang();
					if (null != augment){
						uses.addChild(augment);
					}
				}
			}
			
		}
		if (null != refines){
			int size = this.getRefinesNumber();
			
			for (int i = 0; i < size; i++){
				Refine refine = this.getRefine(i);
				if (null != refine){
					YangStatement ref = refine.writeYang();
					if (null != ref){
						uses.addChild(ref);
					}
				}
			}
		}
		
		return uses;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYin()
	 */
	@Override
	public Element toYin() {
		Element uses = new Element(YangKeyword.USES.getKeyword(),env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		uses.setAttribute(name);
		
		if (!isNullString(this.description)){
			Element descr = new Element(YangStatement.YANG_STATEMENT_DESCRIPTION,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.description);
			descr.addContent(text);
			uses.addContent(descr);
		}
		if (null != this.reference){
			Element ref = new Element(YangStatement.YANG_STATEMENT_REFERENCE,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.reference);
			ref.addContent(text);
			uses.addContent(ref);
		}
		
		if (Status.CURRENT != this.status){
			Element status = new Element(YangStatement.YANG_STATEMENT_STATUS,env.getNs());
			Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),this.getStatus().getStatus());
			status.setAttribute(value);
			uses.addContent(status);
		}
		if (!isNullString(this.when)){
			Element when = new Element(YangKeyword.WHEN.getKeyword(),env.getNs());
			Attribute condition = new Attribute("condition",this.when);
			when.setAttribute(condition);
			uses.addContent(when);
		}		
		if (null != this.if_features){
			int size = this.getIf_featuresNumber();
			
			for (int i = 0; i < size; i++){
				String feature = this.getIf_feature(i);
				if (null != feature){
					Element  featureState = 
							new Element(YangKeyword.IFFEATURE.getKeyword(),env.getNs());
					Attribute fname = new Attribute(XMLTag.XML_TAG_NAME,feature);
					featureState.setAttribute(fname);
					uses.addContent(featureState);
				}
			}
			
		}
		

		if (null != this.augments){
			int size = this.getAugmentsNumber();
			
			for (int i = 0; i < size; i++){
				Augment aug = this.getAugment(i);
				if (null != aug){
					Element augment = aug.writeYin();
					if (null != augment){
						uses.addContent(augment);
					}
				}
			}
			
		}
		if (null != refines){
			int size = this.getRefinesNumber();
			
			for (int i = 0; i < size; i++){
				Refine refine = this.getRefine(i);
				if (null != refine){
					Element ref = refine.writeYin();
					if (null != ref){
						uses.addContent(ref);
					}
				}
			}
		}
		
		return uses;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYin(org.jdom2.Element)
	 */
	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}

		// ����
		if (statement.getName()
				.equals(YangStatement.YANG_STATEMENT_DESCRIPTION)) {
			Element text = statement.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setDescription(text.getText());
		}
		// when
		if (statement.getName().equals(YangKeyword.WHEN.getKeyword())) {
			this.setWhen(statement.getAttributeValue("condition", env.getNs()));
		}
		// reference
		else if (statement.getName().equals(
				YangStatement.YANG_STATEMENT_REFERENCE)) {

			Element text = statement.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setReference(text.getText());
		}
		// status
		else if (statement.getName()
				.equals(YangStatement.YANG_STATEMENT_STATUS)) {
			String value = statement.getAttributeValue(YangKeyword.VALUE
					.getKeyword());
			if (null == value) {
				return MODELELEMENT_FAIL;
			} else {
				this.status = Status.getStatus(value);
			}

		}
		// if-feature
		else if (statement.getName().equals(
				YangStatement.YANG_STATEMENT_IFFEATURE)) {
			String value = statement.getAttributeValue(XMLTag.XML_TAG_NAME);
			if (null == value) {
				return MODELELEMENT_FAIL;
			}
			this.addIf_feature(value);
		}
		//augment
		else if (statement.getName().equals(
				YangKeyword.AUGMENT.getKeyword())) {
			Augment aug = (Augment)ModelElement.parseYin(newSubEnv(), statement);
			this.addAugment(aug);
		}
		//refine
		else if (statement.getName().equals(
				YangKeyword.REFINE.getKeyword())) {
			Refine ref = (Refine)ModelElement.parseYin(newSubEnv(), statement);
			this.addRefine(ref);
		}
		
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.yang.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement subStatement) throws YangParserException, ModelException {
		if (null == subStatement){
			return MODELELEMENT_OK;
		}
		String value = subStatement.getArgument();
		
		//����
		if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			this.setDescription(value);
		}
		//when
		if (subStatement.getKeyword().equals(YangKeyword.WHEN.getKeyword())){
			this.setWhen(value);
		}
	    //reference
		else if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			this.setReference(value);
		}
	    //status
		else if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_STATUS)){
			this.setStatus(Status.getStatus(value));
		}
		// if-feature
		else if (subStatement.getKeyword().equals(
				YangStatement.YANG_STATEMENT_IFFEATURE)) {
			this.addIf_feature(value);
		}
		//augment
		else if (subStatement.getKeyword().equals(YangKeyword.AUGMENT.getKeyword())){
			Augment aug = (Augment)ModelElement.parseYANG(newSubEnv(), subStatement);
			if (null != aug){
				this.addAugment(aug);
			}
		}
		//refine
		else if (subStatement.getKeyword().equals(YangKeyword.REFINE.getKeyword())){
			Refine ref = (Refine)ModelElement.parseYANG(newSubEnv(), subStatement);
			if (null != ref){
				this.addRefine(ref);
			}
		}
		
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#getMapInfo()
	 */
	@Override
	public MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getPath() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getPathByPrefix(String prefix) {
		// TODO Auto-generated method stub
		return null;
	}

	

	@Override
	public String getQualifiedPath() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}

}
